import { MathUtil } from './MathUtil';
/**
 * 三维向量。
 */
export class Vector3 {
    /** X分量 */
    public x: number;
    /** Y分量 */
    public y: number;
    /** Z分量 */
    public z: number;

    public static TOLERANCE = 0.00001;

	/**
	 * 构造处理。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 */
    public constructor(x?: number, y?: number, z?: number) {
        this.x = x || 0;
        this.y = y || 0;
        this.z = z || 0;
    }

    /**
     * 
     * @param vec
     */
    public assign(vec: Vector3) {
        this.x = vec.x;
        this.y = vec.y;
        this.z = vec.z;
    }

	/**
	 * 获得X分量。
	 *
	 * @return X分量
	 */
    public get X(): number {
        return this.x;
    }

	/**
	 * 获得Y分量。
	 *
	 * @return Y分量
	 */
    public get Y(): number {
        return this.y;
    }

	/**
	 * 获得Z分量。
	 *
	 * @return Z分量
	 */
    public get Z(): number {
        return this.z;
    }

	/**
	 * 判断是否有效。
	 *
	 * @return 是否有效
	 */
    public isValid(): boolean {
        return !isNaN(this.x) && !isNaN(this.y) && !isNaN(this.z);
    }

	/**
	 * 判断是否为空。
	 *
	 * @return 是否为空
	 */
    public isEmpty() {
        return (this.x == 0) && (this.y == 0) && (this.z == 0);
    }

	/**
	 * 判断是否近似为空。
	 *
	 * @return 是否为空
	 */
    public isZero(tolerance: number = Vector3.TOLERANCE) {
        return MathUtil.isZero(this.x, tolerance) &&
            MathUtil.isZero(this.y, tolerance) &&
            MathUtil.isZero(this.z, tolerance);
    }

	/**
	 * 判断是否相等。
	 *
	 * @param value 数据
	 * @return 是否相等
	 */
    public equals(value: Vector3): boolean {
        return (this.x == value.x) && (this.y == value.y) && (this.z == value.z);
    }

	/**
	 * 判断是否近似相等。
	 *
	 * @param value 数据
	 * @param tolerance 公差
	 * @return 是否相等
	 */
    public nearlyEquals(value: Vector3, tolerance: number = MathUtil.TOLERANCE): boolean {
        return MathUtil.nearlyEquals(this.x, value.x, tolerance) &&
            MathUtil.nearlyEquals(this.y, value.y, tolerance) &&
            MathUtil.nearlyEquals(this.z, value.z, tolerance);
    }

	/**
	 * 判断是否为负。
	 *
	 * @param value 数据
	 * @return 是否为负
	 */
    public equalsNegative(value: Vector3): boolean {
        return (this.x == -value.x) && (this.y == -value.y) && (this.z == -value.z);
    }

	/**
	 * 判断是否近似为负。
	 *
	 * @param value 数据
	 * @param tolerance 公差
	 * @return 是否为负
	 */
    public nearlyEqualsNegative(value: Vector3, tolerance: number = MathUtil.TOLERANCE): boolean {
        return MathUtil.nearlyEquals(this.x, -value.x, tolerance) &&
            MathUtil.nearlyEquals(this.y, -value.y, tolerance) &&
            MathUtil.nearlyEquals(this.z, -value.z, tolerance);
    }

	/**
	 * 判断是否相等。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 是否相等
	 */
    public equalsValue(x: number, y: number, z: number): boolean {
        return (this.x == x) && (this.y == y) && (this.z == z);
    }

	/**
	 * 判断是否近似相等。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @param tolerance 公差
	 * @return 是否相等
	 */
    public nearlyEqualsValue(x: number, y: number, z: number, tolerance: number = MathUtil.TOLERANCE): boolean {
        return MathUtil.nearlyEquals(this.x, x, tolerance) &&
            MathUtil.nearlyEquals(this.y, y, tolerance) &&
            MathUtil.nearlyEquals(this.z, z, tolerance);
    }

	/**
	 * 设置X分量。
	 *
	 * @param x X分量
	 * @return 向量
	 */
    public setX(x: number): Vector3 {
        this.x = x;
        return this;
    }

	/**
	 * 设置Y分量。
	 *
	 * @param y Y分量
	 * @return 向量
	 */
    public setY(y: number): Vector3 {
        this.y = y;
        return this;
    }

	/**
	 * 设置Z分量。
	 *
	 * @param z Z分量
	 * @return 向量
	 */
    public setZ(z: number): Vector3 {
        this.z = z;
        return this;
    }

	/**
	 * 设置数据。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 向量
	 */
    public set(x: number = 0, y: number = 0, z: number = 0): Vector3 {
        this.x = x;
        this.y = y;
        this.z = z;
        return this;
    }

	/**
	 * 设置数据。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 向量
	 */
    public setO(x: number, y: number, z: number): Vector3 {
        if (x != null) {
            this.x = x;
        }
        if (y != null) {
            this.y = y;
        }
        if (z != null) {
            this.z = z;
        }
        return this;
    }

	/**
	 * 设置数据。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public setScalar(value: number): Vector3 {
        this.x = value;
        this.y = value;
        this.z = value;
        return this;
    }

	/**
	 * 获得组件内容。
	 *
	 * @param index 索引
	 * @return 数据
	 */
    public getComponent(index: number): number {
        switch (index) {
            case 0:
                return this.x;
            case 1:
                return this.y;
            case 2:
                return this.z;
            default:
        }
    }

	/**
	 * 设置组件内容。
	 *
	 * @param index 索引
	 * @param value 数据
	 */
    public setComponent(index: number, value: number) {
        switch (index) {
            case 0:
                this.x = value;
                break;
            case 1:
                this.y = value;
                break;
            case 2:
                this.z = value;
                break;
            default:
            //       throw new Fatal(this, 'Invalid component index.');
        }
    }

    /**
      * to2d。
      *
      * @return 向量
      */
    public to2d(): Vector3 {
        return new Vector3(this.x, this.y, 0);
    }

	/**
	 * 复制数据内容。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public copyTo(value: Vector3): Vector3 {
        value.x = this.x;
        value.y = this.y;
        value.z = this.z;
        return this;
    }

	/**
	 * 加运算处理。
	 *
	 * @param scalar 内容
	 * @return 向量
	 */
    public addScalar(scalar: number): Vector3 {
        this.x += scalar;
        this.y += scalar;
        this.z += scalar;
        return this;
    }

	/**
	 * 单个数据相加处理。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public add(value: Vector3): Vector3 {
        this.x += value.x;
        this.y += value.y;
        this.z += value.z;
        return this;
    }

	/**
	 * 分量数据相加处理。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 向量
	 */
    public addValue(x: number, y: number, z: number): Vector3 {
        this.x += x;
        this.y += y;
        this.z += z;
        return this;
    }

    /**
      * 单个数据相加处理。
      *
      * @param value 数据
      * @return 向量
      */
    public addTo(value: Vector3, ref?: Vector3): Vector3 {
        ref = ref || new Vector3();
        ref.x = this.x + value.x;
        ref.y = this.y + value.y;
        ref.z = this.z + value.z;
        return ref;
    }
    /**
      * 分量数据相加处理。
      *
      * @param x X分量
      * @param y Y分量
      * @param z Z分量
      * @return 向量
      */
    public addValueTo(x: number, y: number, z: number, ref?: Vector3): Vector3 {
        ref = ref || new Vector3();
        ref.x = this.x + x;
        ref.y = this.y + y;
        ref.z = this.z + z;
        return ref;
    }

	/**
	 * 比率数据相加处理。
	 *
	 * @param value 数据
	 * @param scale 比率
	 * @return 向量
	 */
    public addScaledVector(value: Vector3, scale: number) {
        this.x += value.x * scale;
        this.y += value.y * scale;
        this.z += value.z * scale;
        return this;
    }

	/**
	 * 多个数据相加处理。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @return 向量
	 */
    public addVectors(value1: Vector3, value2: Vector3): Vector3 {
        this.x = value1.x + value2.x;
        this.y = value1.y + value2.y;
        this.z = value1.z + value2.z;
        return this;
    }

	/**
	 * 数据相减处理。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public subScalar(value: number): Vector3 {
        this.x -= value;
        this.y -= value;
        this.z -= value;
        return this;
    }

	/**
	 * 单个数据相减处理。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public sub(value: Vector3): Vector3 {
        this.x -= value.x;
        this.y -= value.y;
        this.z -= value.z;
        return this;
    }

	/**
	 * 分量数据相减处理。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 向量
	 */
    public subValue(x: number, y: number, z: number): Vector3 {
        this.x -= x;
        this.y -= y;
        this.z -= z;
        return this;
    }

    /**
      * 单个数据相减处理。
      *
      * @param value 数据
      * @return 向量
      */
    public subTo(value: Vector3, ref?: Vector3): Vector3 {
        ref = ref || new Vector3();
        ref.x = this.x - value.x;
        ref.y = this.y - value.y;
        ref.z = this.z - value.z;
        return ref;
    }

	/**
	 * 分量数据相减处理。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 向量
	 */
    public subValueTo(x: number, y: number, z: number, ref?: Vector3): Vector3 {
        ref = ref || new Vector3();
        ref.x = this.x - x;
        ref.y = this.y - y;
        ref.z = this.z - z;
        return ref;
    }

	/**
	 * 多个数据相减处理。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @return 向量
	 */
    public subVectors(value1: Vector3, value2: Vector3): Vector3 {
        this.x = value1.x - value2.x;
        this.y = value1.y - value2.y;
        this.z = value1.z - value2.z;
        return this;
    }

	/**
	 * 数据相乘处理。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public multiplyScalar(value: number): Vector3 {
        this.x *= value;
        this.y *= value;
        this.z *= value;
        return this;
    }

	/**
	 * 单个数据相乘处理。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public multiply(value: Vector3): Vector3 {
        this.x *= value.x;
        this.y *= value.y;
        this.z *= value.z;
        return this;
    }

	/**
	 * 分量数据相乘处理。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 向量
	 */
    public multiplyValue(x: number, y: number, z: number): Vector3 {
        this.x *= x;
        this.y *= y;
        this.z *= z;
        return this;
    }

	/**
	 * 多个数据相乘处理。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @return 向量
	 */
    public multiplyVectors(value1: Vector3, value2: Vector3): Vector3 {
        this.x = value1.x * value2.x;
        this.y = value1.y * value2.y;
        this.z = value1.z * value2.z;
        return this;
    }

	/**
	 * 除运算处理。
	 *
	 * @param scalar 内容
	 * @return 向量
	 */
    public divideScalar(value: number): Vector3 {
        var scalar = 1 / value;
        return this.multiplyScalar(scalar);
    }

	/**
	 * 单个数据相除处理。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public divide(value: Vector3): Vector3 {
        this.x /= value.x;
        this.y /= value.y;
        this.z /= value.z;
        return this;
    }

	/**
	 * 分量数据相除处理。
	 *
	 * @param x X分量
	 * @param y Y分量
	 * @param z Z分量
	 * @return 向量
	 */
    public divideValue(x: number, y: number, z: number): Vector3 {
        this.x /= x;
        this.y /= y;
        this.z /= z;
        return this;
    }

	/**
	 * 获得长度。
	 *
	 * @return 长度
	 */
    public length(): number {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }

	/**
	 * 获得平方长度。
	 *
	 * @return 长度
	 */
    public lengthSq(): number {
        return this.x * this.x + this.y * this.y + this.z * this.z;
    }

	/**
	 * 获得平方长度。
	 *
	 * @return 长度
	 */
    public lengthSquared(): number {
        return this.x * this.x + this.y * this.y + this.z * this.z;
    }

	/**
	 * 获得模长度。
	 *
	 * @return 长度
	 */
    public lengthManhattan(): number {
        return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z);
    }

	/**
	 * 设置长度。
	 *
	 * @param length 长度
	 * @return 向量
	 */
    public setLength(length: number): Vector3 {
        var currentLength = this.length();
        var rate = length / currentLength;
        return this.multiplyScalar(rate);
    }

	/**
	 * 单位化处理。
	 *
	 * @return 向量
	 */
    public normalize(): Vector3 {
        var length = this.length();
        return this.divideScalar(length);
    }

	/**
	 * 平面单位化处理。
	 *
	 * @return 向量
	 */
    public normalize2(): Vector3 {
        var length = Math.sqrt(this.x * this.x + this.y * this.y);
        var scale = 1 / length;
        this.x *= scale;
        this.y *= scale;
        return this;
    }

	/**
	 * 计算取反的数据。
	 *
	 * @return 向量
	 */
    public negate(): Vector3 {
        this.x = -this.x;
        this.y = -this.y;
        this.z = -this.z;
        return this;
    }

	/**
	 * 计算向下取整的数据。
	 *
	 * @return 向量
	 */
    public floor(): Vector3 {
        this.x = Math.floor(this.x);
        this.y = Math.floor(this.y);
        this.z = Math.floor(this.z);
        return this;
    }

	/**
	 * 计算向上取整的数据。
	 *
	 * @return 向量
	 */
    public ceil(): Vector3 {
        this.x = Math.ceil(this.x);
        this.y = Math.ceil(this.y);
        this.z = Math.ceil(this.z);
        return this;
    }

	/**
	 * 计算向0取整的数据。
	 *
	 * @return 向量
	 */
    public roundToZero() {
        this.x = (this.x < 0) ? Math.ceil(this.x) : Math.floor(this.x);
        this.y = (this.y < 0) ? Math.ceil(this.y) : Math.floor(this.y);
        this.z = (this.z < 0) ? Math.ceil(this.z) : Math.floor(this.z);
        return this;
    }

	/**
	 * 获得朝向指定点的角度。
	 *
	 * @param point 顶点
	 * @return 角度
	 */
    public angleTo(point: Vector3): number {
        var dotValue = this.dot(point);
        var theta = dotValue / (Math.sqrt(this.lengthSquared() * point.lengthSquared()));
        return Math.acos(MathUtil.clamp(theta, - 1, 1));
    }

	/**
	 * 获得到指定点的距离。
	 *
	 * @param point 顶点
	 * @return 距离
	 */
    public distanceTo(point: Vector3): number {
        return Math.sqrt(this.distanceToSquared(point));
    }

	/**
	 * 获得到指定点的距离。
	 *
	 * @param point 顶点
	 * @return 距离
	 */
    public distanceToSquared(point: Vector3): number {
        var dx = this.x - point.x;
        var dy = this.y - point.y;
        var dz = this.z - point.z;
        return dx * dx + dy * dy + dz * dz;
    }

	/**
	 * 设置最小数据。
	 */
    public setMin() {
        this.x = this.y = this.z = Number.MIN_VALUE;
    }

	/**
	 * 计算最小数据。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public min(value: Vector3): Vector3 {
        if (value.x < this.x) {
            this.x = value.x;
        }
        if (value.y < this.y) {
            this.y = value.y;
        }
        if (value.z < this.z) {
            this.z = value.z;
        }
        return this;
    }

	/**
	 * 计算最小数据。
	 *
	 * @param x X坐标
	 * @param y Y坐标
	 * @param z Z坐标
	 * @return 向量
	 */
    public minValue(x: number, y: number, z: number): Vector3 {
        if (x < this.x) {
            this.x = x;
        }
        if (y < this.y) {
            this.y = y;
        }
        if (z < this.z) {
            this.z = z;
        }
        return this;
    }

	/**
	 * 设置最大数据。
	 */
    public setMax() {
        this.x = this.y = this.z = Number.MAX_VALUE;
    }

	/**
	 * 计算最大数据。
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public max(value: Vector3): Vector3 {
        if (value.x > this.x) {
            this.x = value.x;
        }
        if (value.y > this.y) {
            this.y = value.y;
        }
        if (value.z > this.z) {
            this.z = value.z;
        }
        return this;
    }

	/**
	 * 计算最大数据。
	 *
	 * @param x X坐标
	 * @param y Y坐标
	 * @param z Z坐标
	 * @return 向量
	 */
    public maxValue(x: number, y: number, z: number): Vector3 {
        if (x > this.x) {
            this.x = x;
        }
        if (y > this.y) {
            this.y = y;
        }
        if (z > this.z) {
            this.z = z;
        }
        return this;
    }

	/**
	 * 计算最小和最大之间数据。
	 *
	 * @param min 最小数据
	 * @param max 最大数据
	 * @return 向量
	 */
    public clamp(min: Vector3, max: Vector3): Vector3 {
        this.x = Math.max(min.x, Math.min(max.x, this.x));
        this.y = Math.max(min.y, Math.min(max.y, this.y));
        this.z = Math.max(min.z, Math.min(max.z, this.z));
        return this;
    }

	/**
	 * 计算最小和最大之间数据。
	 *
	 * @param min 最小数据
	 * @param max 最大数据
	 * @return 向量
	 */
    public clampScalar(min: number, max: number): Vector3 {
        this.x = Math.max(min, Math.min(max, this.x));
        this.y = Math.max(min, Math.min(max, this.y));
        this.z = Math.max(min, Math.min(max, this.z));
        return this;
    }

	/**
	 * 计算最小和最大之间长度。
	 *
	 * @param min 最小长度
	 * @param max 最大长度
	 * @return 向量
	 */
    public clampLength(min, max): Vector3 {
        var length = this.length();
        var rate = Math.max(min, Math.min(max, length)) / length;
        return this.multiplyScalar(rate);
    }

	/**
	 * 计算点积处理。已知 力和位移 求功
	 * 单位化后 的dot值 是两个向量夹角的cos值
	 * @param value 数据
	 * @return 数据
	 */
    public dot(value: Vector3): number {
        return this.x * value.x + this.y * value.y + this.z * value.z;
    }


	/**
	 * 计算叉积处理。 得到垂直于两个矢量的矢量
	 *
	 * @param value 数据
	 * @return 向量
	 */
    public cross(value: Vector3): Vector3 {
        var x = this.x;
        var y = this.y;
        var z = this.z;
        this.x = y * value.z - z * value.y;
        this.y = z * value.x - x * value.z;
        this.z = x * value.y - y * value.x;
        return this;
    }

	/**
	 * 计算叉积处理。
	 *
	 * @param left 数据1
	 * @param right 数据2
	 * @return 向量
	 */
    public crossVectors(left: Vector3, right: Vector3): Vector3 {
        var ax = left.x;
        var ay = left.y;
        var az = left.z;
        var bx = right.x;
        var by = right.y;
        var bz = right.z;
        this.x = ay * bz - az * by;
        this.y = az * bx - ax * bz;
        this.z = ax * by - ay * bx;
        return this;
    }

	/**
	* 三维向量的叉乘，返回与这个向量和另一个向量相正交的向量
	* @param value 另一个向量
	*/
    public crossVector(value: Vector3): Vector3 {
        var vec = this as Vector3;
        vec.cross(value);
        return vec;
    }

	/**
	 * 计算两个空间向量的夹角
	 *
	 */
    public angle(value: Vector3): number {
        var crs = this.dot(value) as number;
        var mod = (this.length() * value.length()) as number;
        var cos = crs / (mod + MathUtil.EPSILON);

        if (cos < -1.0)
            cos = -1.0;
        else if (cos > 1.0)
            cos = 1.0;

        return Math.acos(cos);
    }

    /**
    * 计算带参考向量的两个空间向量的夹角
    *
    */
    public angleDeg(value: Vector3, refVec: Vector3): number {

        if (refVec.dot(this.cross(value)) >= 0.0)
            return this.angle(value);
        else
            return Math.PI * 2 - this.angle(value);
    }

	/**
	 * 计算插值处理。
	 *
	 * @param value 数据
	 * @param rate 比率
	 * @return 向量
	 */
    public lerp(value: Vector3, rate: number): Vector3 {
        this.x += (value.x - this.x) * rate;
        this.y += (value.y - this.y) * rate;
        this.z += (value.z - this.z) * rate;
        return this;
    }

	/**
	 * 计算插值处理。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @param rate 比率
	 * @return 向量
	 */
    public lerpVectors(value1: Vector3, value2: Vector3, rate: number): Vector3 {
        this.subVectors(value2, value1).multiplyScalar(rate).add(value1);
        return this;
    }

    /**
     * X轴反转处理。
     *
      * @return 向量
     */
    public flipX(): Vector3 {
        this.x = -this.x;
        return this;
    }

    /**
     * Y轴反转处理。
     *
      * @return 向量
     */
    public flipY(): Vector3 {
        this.y = -this.y;
        return this;
    }

    /**
     * Z轴反转处理。
     *
      * @return 向量
     */
    public flipZ(): Vector3 {
        this.z = -this.z;
        return this;
    }

	/**
	 * 从数组中获得数据。
	 *
	 * @param array 数组
	 * @param offset 位置
	 * @return 向量
	 */
    public fromArray(array: Array<number>, offset: number = 0): Vector3 {
        this.x = array[offset];
        this.y = array[offset + 1];
        this.z = array[offset + 2];
        return this;
    }

	/**
	 * 设置数据到数组中
	 *
	 * @param array 数组
	 * @param offset 位置
	 * @return 数组
	 */
    public toArray(array?: Array<number>, offset: number = 0): Array<number> {
        if (array == null) {
            array = new Array<number>();
        }
        array[offset] = this.x;
        array[offset + 1] = this.y;
        array[offset + 2] = this.z;
        return array;
    }

	/**
	 * 获得字符串。
	 *
	 * @return 字符串
	 */
    public toString(): string {
        return this.x + ',' + this.y + ',' + this.z;
    }

	/**
	 * 释放内容。
	 *
	 * @return 向量
	 */
    public free(): Vector3 {
        this.x = 0;
        this.y = 0;
        this.z = 0;
        return this;
    }

	/**
	 * 克隆当前数据。
	 *
	 * @return 向量
	 */
    public clone(type?: Function): Vector3 {
        var constructor = type || (this as any).constructor;
        return new constructor(this.x, this.y, this.z);
    }

	/**
	 * 重置数据。
	 *
	 * @return 向量
	 */
    public reset(): Vector3 {
        this.x = 0;
        this.y = 0;
        this.z = 0;
        return this;
    }

	/**
	 * 释放处理。
	 */
    public dispose() {
        this.x = null;
        this.y = null;
        this.z = null;
    }

    /** 原点 */
    public static Origin = new Vector3(0, 0, 0);
    /** 轴X */
    public static AxisX = new Vector3(1, 0, 0);
    /** 负轴X */
    public static AxisNX = new Vector3(-1, 0, 0);
    /** 轴Y */
    public static AxisY = new Vector3(0, 1, 0);
    /** 负轴Y */
    public static AxisNY = new Vector3(0, -1, 0);
    /** 轴Z */
    public static AxisZ = new Vector3(0, 0, 1);
    /** 负轴Z */
    public static AxisNZ = new Vector3(0, 0, -1);
    /** 单位 */
    public static Unit = new Vector3(1, 1, 1);

	/**
	 * 从UVW维度构造三维向量。
	 *
	 * @param u U维度
	 * @param x X分量
	 * @param v V维度
	 * @param y Y分量
	 * @param w W维度
	 * @param z Z分量
	 * @return 向量
	 */

    public static fromUvw(u: string, x: number, v: string, y: number, w: string, z: number): Vector3 {
        var value = new Vector3();
        value[u] = x;
        value[v] = y;
        value[w] = z;
        return value;
    }

	/**
	 * 判断是否相等。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @return 是否相等
	 */
    public static equals(value1: Vector3, value2: Vector3): boolean {
        return (value1.x == value2.x) && (value1.y == value2.y) && (value1.z == value2.z);
    }

	/**
	 * 判断是否近似相等。
	 *
	 * @param value 数据
	 * @param tolerance 公差
	 * @return 是否相等
	 */
    public static nearlyEquals(value1: Vector3, value2: Vector3, tolerance: number = MathUtil.TOLERANCE): boolean {
        var ex = MathUtil.nearlyEquals(value1.x, value2.x, tolerance);
        var ey = MathUtil.nearlyEquals(value1.y, value2.y, tolerance);
        var ez = MathUtil.nearlyEquals(value1.z, value2.z, tolerance);
        return ex && ey && ez;
    }

	/**
	 * 判断是否为负。
	 *
	 * @param value 数据
	 * @return 是否为负
	 */
    public static equalsNegative(value1: Vector3, value2: Vector3): boolean {
        return (value1.x == -value2.x) && (value1.y == -value2.y) && (value1.z == -value2.z);
    }

	/**
	 * 判断是否近似为负。
	 *
	 * @param value 数据
	 * @param tolerance 公差
	 * @return 是否为负
	 */
    public static nearlyEqualsNegative(value1: Vector3, value2: Vector3, tolerance: number = MathUtil.TOLERANCE): boolean {
        return MathUtil.nearlyEquals(value1.x, -value2.x, tolerance) &&
            MathUtil.nearlyEquals(value1.y, -value2.y, tolerance) &&
            MathUtil.nearlyEquals(value1.z, -value2.z, tolerance);
    }

	/**
	 * 数据相加处理。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @param target 目标
	 * @return 向量
	 */
    public static add(value1: Vector3, value2: Vector3, target?: Vector3): Vector3 {
        var result = target || new Vector3();
        result.x = value1.x + value2.x;
        result.y = value1.y + value2.y;
        result.z = value1.z + value2.z;
        return result;
    }

	/**
	 * 多个数据相加处理。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @return 向量
	 */
    public static addVectors(value1: Vector3, value2: Vector3): Vector3 {
        var cx = value1.x + value2.x;
        var cy = value1.y + value2.y;
        var cz = value1.z + value2.z;
        return new Vector3(cx, cy, cz);
    }

	/**
	 * 数据相减处理。
	 *
	 * @param value1 数据1
	 * @param value2 数据2
	 * @param target 目标
	 * @return 向量
	 */
    public static sub(value1: Vector3, value2: Vector3, target?: Vector3): Vector3 {
        var result = target || new Vector3();
        result.x = value1.x - value2.x;
        result.y = value1.y - value2.y;
        result.z = value1.z - value2.z;
        return result;
    }

	/**
	 * 获得两点之间距离。
	 *
	 * @param point1 开始点
	 * @param point2 结束点
	 * @return 距离
	 */
    public static distance(point1: Vector3, point2: Vector3): number {
        var cx = point2.x - point1.x;
        var cy = point2.y - point1.y;
        var cz = point2.z - point1.z;
        return Math.sqrt(cx * cx + cy * cy + cz * cz)
    }

	/**
	 * 获得两点之间距离。
	 *
	 * @param x1 开始点X坐标
	 * @param y1 开始点Y坐标
	 * @param z1 开始点Z坐标
	 * @param x2 结束点X坐标
	 * @param y2 结束点Y坐标
	 * @param z2 结束点Z坐标
	 * @return 距离
	 */
    public static distanceValue(x1: number, y1: number, z1: number, x2: number, y2: number, z2: number): number {
      //  AssertUtil.debugNumber(x1, y1, z1, x2, y2, z2);
        var cx = x1 - x2;
        var cy = y1 - y2;
        var cz = z1 - z2;
        return Math.sqrt(cx * cx + cy * cy + cz * cz)
    }

	/**
	 * 获得方向处理。
	 *
	 * @param point1 开始点
	 * @param point2 结束点
	 * @return 方向
	 */
    public static direction(start: Vector3, end: Vector3): Vector3 {
        var cx = end.x - start.x;
        var cy = end.y - start.y;
        var cz = end.z - start.z;
        var direction = new Vector3(cx, cy, cz);
        direction.normalize();
        return direction;
    }

	/**
	 * 获得两个点的中心点。
	 *
	 * @param point1 点1
	 * @param point2 点2
	 * @return 中心点
	 */
    public static middle(point1: Vector3, point2: Vector3): Vector3 {
        var x = point1.x + point2.x;
        var y = point1.y + point2.y;
        var z = point1.z + point2.z;
        return new Vector3(x * 0.5, y * 0.5, z * 0.5);
    }


	/**
	 * 计算差乘处理。
	 *
	 * @param left 数据1
	 * @param right 数据2
	 * @param target 目标
	 * @return 方向
	 */
    public static crossVectors(left: Vector3, right: Vector3, target?: Vector3): Vector3 {
        var result = target || new Vector3();
        result.crossVectors(left, right);
        return result;
    }

	/**
	 * 判断是否顶点集合中是否含有点。
	 *
	 * @param points 点集合
	 * @param point 点
	 * @returns 是否含有
	 */
    public static contains(points: Array<Vector3>, point: Vector3): boolean {
        var count = points.length;
        for (var i = 0; i < count; i++) {
            var find = points[i];
            if (Vector3.equals(find, point)) {
                return true;
            }
        }
        return false;
    }

	/**
	 * 判断是否顶点集合中是否近似含有点。
	 *
	 * @param points 点集合
	 * @param point 点
	 * @returns 是否含有
	 */
    public static nearlyContains(points: Array<Vector3>, point: Vector3, tolerance: number = MathUtil.TOLERANCE): boolean {
        var count = points.length;
        for (var i = 0; i < count; i++) {
            var find = points[i];
            if (Vector3.nearlyEquals(find, point, tolerance)) {
                return true;
            }
        }
        return false;
    }

	/**
	 * 从集合中获得指定点的索引。
	 *
	 * @param points 点集合
	 * @param point 点
	 * @returns 索引位置
	 */
    public static indexOf(points: Array<Vector3>, point: Vector3): number {
        var repeat = false;
        var count = points.length;
        for (var i = 0; i < count; i++) {
            var find = points[i];
            if (Vector3.equals(find, point)) {
                return i;
            }
        }
        return -1;
    }

	/**
	 * 从集合中获得指定点的索引。
	 *
	 * @param points 点集合
	 * @param point 点
	 * @returns 索引位置
	 */
    public static nearlyIndexOf(points: Array<Vector3>, point: Vector3, tolerance: number = MathUtil.TOLERANCE): number {
        var repeat = false;
        var count = points.length;
        for (var i = 0; i < count; i++) {
            var find = points[i];
            if (Vector3.nearlyEquals(find, point, tolerance)) {
                return i;
            }
        }
        return -1;
    }

	/**
	 * 获得非重复的点集合。
	 *
	 * @param points 点集合
	 * @param tolerance 容差
	 * @returns 非重复点集合
	 */
    public static unique(points: Array<Vector3>, tolerance: number = MathUtil.TOLERANCE) {
        var result = new Array<Vector3>();
        var count = points.length;
        for (var j = 0; j < count; j++) {
            var point = points[j];
            if (!Vector3.nearlyContains(result, point)) {
                result.push(point);
            }
        }
        return result;
    }


    public transformDirection(m) {
        var x = this.x, y = this.y, z = this.z;
        var e = m.elements;
        this.x = e[0] * x + e[4] * y + e[8] * z;
        this.y = e[1] * x + e[5] * y + e[9] * z;
        this.z = e[2] * x + e[6] * y + e[10] * z;
        return this.normalize();
    }

    public setFromSpherical(s) {
        var sinPhiRadius = Math.sin(s.phi) * s.radius;
        this.x = sinPhiRadius * Math.sin(s.theta);
        this.y = Math.cos(s.phi) * s.radius;
        this.z = sinPhiRadius * Math.cos(s.theta);
        return this;
    }

    public setFromMatrixPosition(m) {
        return this.setFromMatrixColumn(m, 3);
    }

    public setFromMatrixScale(m) {
        var sx = this.setFromMatrixColumn(m, 0).length();
        var sy = this.setFromMatrixColumn(m, 1).length();
        var sz = this.setFromMatrixColumn(m, 2).length();
        this.x = sx;
        this.y = sy;
        this.z = sz;
        return this;
    }

    public setFromMatrixColumn(m, index) {
        if (typeof m === 'number') {
            console.warn('setFromMatrixColumn now expects ( matrix, index ).');
            var temp = m
            m = index;
            index = temp;
        }
        return this.fromArray(m.elements, index * 4);
    }

	/**
	 * 计算一个线段和四角面相交的焦点。
	 *
	 * @param box1 盒子1
	 * @param box2 盒子2
	 * @param target 目标盒子
	 * @return 相交盒子
	 */
    public static intersectFace4(line: Array<Vector3>, face: Array<Vector3>, target?: Vector3): Vector3 {
        var result = target || new Vector3();
        return result;
    }
}
